home *** CD-ROM | disk | FTP | other *** search
/ Windows Expert / Windows Expert.iso / windownt / xvidiff.zip / NT.C < prev    next >
C/C++ Source or Header  |  1993-01-02  |  21KB  |  988 lines

  1. /***
  2.  
  3. * program name:
  4.     xvi
  5. * function:
  6.     PD version of UNIX "vi" editor, with extensions.
  7. * module name:
  8.     nt.c
  9. * module function:
  10.     System interface routines for Windows NT.
  11. * history:
  12.     STEVIE - ST Editor for VI Enthusiasts, Version 3.10
  13.     Originally by Tim Thompson (twitch!tjt)
  14.     Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
  15.     Heavily modified by Chris & John Downey
  16.  
  17. ***/
  18.  
  19. #include "xvi.h"
  20.  
  21. #undef va_start
  22. #undef va_end
  23.  
  24. #include <windows.h>
  25.  
  26. #define PERR(r,api) {if(!(r)) perr(__FILE__, __LINE__, api, GetLastError());}
  27. #define BACKGROUND_WHITE (BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE)
  28. #define FOREGROUND_WHITE (FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE)
  29.  
  30. void perr(PCHAR, int, PCHAR, DWORD );
  31. void setconssize(HANDLE ,int ,int );
  32. int getConX(HANDLE);
  33. int getConY(HANDLE);
  34. void settextcolor(void);
  35. int virtkey(KEY_EVENT_RECORD);
  36.  
  37. int Rows, Columns;
  38.  
  39. static COORD Currpos;
  40. static SMALL_RECT WindowRect;
  41. static COORD WindowSize;
  42. static HANDLE hStdIn, hStdOut;
  43. static WORD Currcolor = FOREGROUND_GREEN;
  44.  
  45. void
  46. popupmsg(char *s)
  47. {
  48.     perr(__FILE__, __LINE__, s, 0 );
  49. }
  50.  
  51. BOOL
  52. myhandler(DWORD dwCtrlType)
  53. {
  54.     switch(dwCtrlType){
  55.  
  56.     case CTRL_C_EVENT:
  57.         /* used to be: kbdintr = 1; */
  58.         (void) do_preserve();
  59.         exit(0);
  60.         return TRUE;
  61.  
  62.     case CTRL_BREAK_EVENT:
  63.     case CTRL_CLOSE_EVENT:
  64.         (void) do_preserve();
  65.         exit(0);
  66.         return TRUE;
  67.     }
  68.     return FALSE;
  69. }
  70.  
  71. void
  72. ignore_signals(void)
  73. {
  74.     SetConsoleCtrlHandler((PHANDLER_ROUTINE) &myhandler,FALSE);
  75. }
  76.  
  77. void
  78. catch_signals(void)
  79. {
  80.     SetConsoleCtrlHandler((PHANDLER_ROUTINE) &myhandler,TRUE);
  81. }
  82.  
  83. void
  84. flush_output(void)
  85. {
  86. }
  87.  
  88. void
  89. alert(void)
  90. {
  91.     Beep(600,200);
  92. }
  93.  
  94. void
  95. sys_init(void)
  96. {
  97.     BOOL r;
  98.     DWORD dwMode;
  99.  
  100.     r = FreeConsole();
  101.     if ( !r ) {
  102.         Beep(600,200);
  103.         Beep(1200,200);
  104.         exit(1);
  105.     }
  106.  
  107.     r = AllocConsole();
  108.     if ( !r ) {
  109.         Beep(1200,200);
  110.         Beep(600,200);
  111.         exit(1);
  112.     }
  113.     r = SetConsoleTitle("XVI - Windows NT Version");
  114.     PERR(r,"SetConsoleTitle");
  115.  
  116.     PERR(1,"PERR TEST");
  117.  
  118.     /* Get standard Handles */
  119.  
  120.     hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
  121.     PERR(hStdOut != INVALID_HANDLE_VALUE, "GetStdHandle");
  122.  
  123.     hStdIn = GetStdHandle(STD_INPUT_HANDLE);
  124.     PERR(hStdIn != INVALID_HANDLE_VALUE, "GetStdHandle");
  125.  
  126.     /* set up mouse and window input */
  127.     r = GetConsoleMode(hStdIn, &dwMode);
  128.     PERR(r, "GetConsoleMode");
  129.  
  130.     /* when turning off ENABLE_LINE_INPUT, you MUST also turn off */
  131.     /* ENABLE_ECHO_INPUT. */
  132.     r = SetConsoleMode(hStdIn, (dwMode & ~(ENABLE_LINE_INPUT |
  133.         ENABLE_ECHO_INPUT))| ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
  134.     PERR(r, "SetConsoleMode");
  135.  
  136.     setconssize(hStdOut,80,24);
  137. }
  138.  
  139. void
  140. setconssize(HANDLE hConsole,int xsize,int ysize)
  141. {
  142.     WORD wConBufSize;
  143.     BOOL r;
  144.  
  145.     Rows = ysize;
  146.     Columns = xsize;
  147.  
  148.     /* The order of resizing buffer vs. resizing window is dependent on */
  149.     /* whether we're currently larger or smaller than the desired size. */
  150.  
  151.     wConBufSize = getConX(hConsole) * getConY(hConsole);
  152.     WindowRect.Left = 0;
  153.     WindowRect.Top = 0;
  154.     WindowRect.Right = xsize-1;
  155.     WindowRect.Bottom = ysize-1;
  156.     WindowSize.X = xsize;
  157.     WindowSize.Y = ysize;
  158.     
  159.     if ( wConBufSize > (xsize*ysize) ) {
  160.         r = SetConsoleWindowInfo(hConsole, TRUE, &WindowRect);
  161.         PERR(r,"SetConsoleWindowInfo");
  162.         r = SetConsoleScreenBufferSize(hConsole, WindowSize);
  163.         PERR(r,"SetConsoelScreenBufferSize");
  164.     }
  165.     else if ( wConBufSize < (xsize*ysize) ) {
  166.         r = SetConsoleScreenBufferSize(hConsole, WindowSize);
  167.         PERR(r,"SetConsoelScreenBufferSize");
  168.         r = SetConsoleWindowInfo(hConsole, TRUE, &WindowRect);
  169.         PERR(r,"SetConsoleWindowInfo");
  170.     }
  171.  
  172.     erase_display();
  173.     set_colour(1);
  174.     settextcolor();
  175. }
  176.  
  177. void
  178. settextcolor()
  179. {
  180.     int r;
  181.  
  182.     /* set attributes for new writes */
  183.     r = SetConsoleTextAttribute(hStdOut,Currcolor);
  184.     PERR(r,"SetConsoleTextAttribute");
  185. }
  186.  
  187. void
  188. outchar(int c)
  189. {
  190.     char s[2];
  191.     s[0] = c;
  192.     s[1] = '\0';
  193.     outstr(s);
  194. }
  195.  
  196. void
  197. outstr(char *s)
  198. {
  199.     DWORD cCharsWritten;
  200.     int r;
  201.  
  202.     r = WriteFile(hStdOut,s,strlen(s),&cCharsWritten, NULL);
  203.     PERR(r,"WriteFile(hStdOut...)");
  204.     
  205. }
  206.  
  207. void
  208. erase_display(void)
  209. {
  210.     int r;
  211.     COORD pos = { 0, 0 };
  212.     DWORD nwritten;
  213.  
  214.     r = FillConsoleOutputAttribute(hStdOut, 0,
  215.         WindowSize.X*WindowSize.Y, pos, &nwritten);
  216.     PERR(r,"FillConsoleOutputAttribute");
  217. }
  218.  
  219. void
  220. erase_line(void)
  221. {
  222.     int nblanks;
  223.     int r;
  224.     DWORD nwritten;
  225.  
  226.     nblanks = WindowSize.X - Currpos.X;
  227.     r = FillConsoleOutputAttribute(hStdOut,0,nblanks,Currpos,&nwritten);
  228.     PERR(r,"FillConsoleOutputAttribute");
  229. }
  230.  
  231. void
  232. tty_goto(int r, int c)
  233. {
  234.     int ret;
  235.  
  236.     Currpos.X = c;
  237.     Currpos.Y = r;
  238.     ret = SetConsoleCursorPosition(hStdOut,Currpos);
  239.     PERR(ret,"SetConsoleCursorPosition");
  240. }
  241.  
  242. int
  243. getConX(HANDLE hcon)
  244. {
  245.     CONSOLE_SCREEN_BUFFER_INFO csbi;
  246.     BOOL r;
  247.  
  248.     r = GetConsoleScreenBufferInfo(hcon, &csbi);
  249.     PERR(r, "GetConsoleScreenBufferInfo");
  250.     return(csbi.dwSize.X);
  251. }
  252.  
  253. int
  254. getConY(HANDLE hcon)
  255. {
  256.     CONSOLE_SCREEN_BUFFER_INFO csbi;
  257.     BOOL r;
  258.  
  259.     r = GetConsoleScreenBufferInfo(hcon, &csbi);
  260.     PERR(r, "GetConsoleScreenBufferInfo");
  261.     return(csbi.dwSize.Y);
  262. }
  263.  
  264. static    enum {
  265.     m_SYS = 0,
  266.     m_VI
  267. }    curmode;
  268.  
  269. /*
  270.  * Set up video state for editor.
  271.  */
  272. void
  273. sys_startv(void)
  274. {
  275. }
  276.  
  277. /*
  278.  * Restore video state to what it was when we started.
  279.  */
  280. void
  281. sys_endv(void)
  282. {
  283. }
  284.  
  285. void
  286. sys_exit(int r)
  287. {
  288.     sys_endv();
  289.     exit(r);
  290. }
  291.  
  292. void
  293. sleep(n)
  294. unsigned    n;
  295. {
  296.     flush_output();
  297.     _sleep(n*1000);
  298. }
  299.  
  300. void
  301. delay()
  302. {
  303.     clock_t start = clock();
  304.  
  305.     flush_output();
  306.     while (clock() < start + CLOCKS_PER_SEC / 5)
  307.     ;
  308. }
  309.  
  310. /*
  311.  * This function is only used by tempfname(). It constructs a filename
  312.  * suffix based on an index number.
  313.  *
  314.  * The suffix ".$$$" is commonly used for temporary file names on
  315.  * MS-DOS & OS/2 systems. We also use the sequence ".$$1", ".$$2" ...
  316.  * ".fff" (all digits are hexadecimal).
  317.  */
  318. static char *
  319. hexsuffix(i)
  320. unsigned    i;
  321. {
  322.     static char    suffix [] = ".$$$";
  323.     static char    hextab [] = "0123456789abcdef";
  324.     char*        sp = &suffix[3];
  325.  
  326.     while (sp > suffix) {
  327.     if (i > 0) {
  328.         *sp-- = hextab [i & 0xf];
  329.         i >>= 4;
  330.     } else {
  331.         *sp-- = '$';
  332.     }
  333.     }
  334.     return suffix;
  335. }
  336.  
  337. /*
  338.  * Construct unique name for temporary file, to be used as a backup
  339.  * file for the named file.
  340.  */
  341. char*
  342. tempfname(srcname)
  343. char        *srcname;
  344. {
  345.     char    *srctail,
  346.         *srcdot,
  347.         *endp,
  348.         *retp;
  349.     unsigned    indexnum = 0;
  350.     unsigned    baselen;
  351.  
  352.     srctail = srcdot = NULL;
  353.     endp = srcname;
  354.  
  355.     while (*endp) {
  356.     switch (*endp++) {
  357.     case '\\':
  358.     case '/':
  359.         srctail = endp;
  360.         srcdot = (char*) 0;
  361.         continue;
  362.     case '.':
  363.         srcdot = endp - 1;
  364.     }
  365.     }
  366.  
  367.     if (srctail == NULL) {
  368.     /*
  369.      * We haven't found any directory separators ('/' or '\\').
  370.      */
  371.     srctail = srcname;
  372.     /*
  373.      * Check to see if there's a disk drive name. If there
  374.      * is, skip over it.
  375.      */
  376.     if (*srcname && is_alpha(*srcname) && srcname[1] == ':') {
  377.         srctail = &srcname[2];
  378.     }
  379.     }
  380.  
  381.     /*
  382.      * There isn't a dot in the trailing part of the filename:
  383.      * just add it at the end.
  384.      */
  385.     if (srcdot == NULL) {
  386.     srcdot = endp;
  387.     }
  388.  
  389.     /*
  390.      * Don't make name too long.
  391.      */
  392.     if (srcdot - srctail > MAXNAMLEN - 4)
  393.     srcdot = srctail + MAXNAMLEN - 4;
  394.     if (srcdot - srcname > MAXPATHLEN - 4)
  395.     srcdot = srcname + MAXPATHLEN - 4;
  396.     baselen = srcdot - srcname;
  397.     /*
  398.      * Allocate space for new temporary file name ...
  399.      */
  400.     if ((retp = alloc(baselen + 5)) == NULL)
  401.     return NULL;
  402.     if (baselen > 0)
  403.     (void) memcpy(retp, srcname, baselen);
  404.     do {
  405.     /*
  406.      * Keep trying this until we get a unique file name.
  407.      */
  408.     strcpy(&retp[baselen], hexsuffix(indexnum++));
  409.     } while (exists(retp));
  410.     return retp;
  411. }
  412.  
  413. /*
  414.  * Fake out a pipe by writing output to temp file, running a process with
  415.  * i/o redirected from this file to another temp file, and then reading
  416.  * the second temp file back in.
  417.  */
  418. bool_t
  419. sys_pipe(cmd, writefunc, readfunc)
  420. char    *cmd;
  421. int    (*writefunc) P((FILE *));
  422. long    (*readfunc) P((FILE *));
  423. {
  424.     char    *temp1;
  425.     FILE    *fp;
  426.     bool_t    retval;
  427.  
  428.     /*
  429.      * Create first temporary file ...
  430.      */
  431.     if ((temp1 = tempfname("xvi_out")) == NULL ||
  432.                     (fp = fopen(temp1, "w")) == NULL) {
  433.     retval = FALSE;
  434.     } else {
  435.     char    *temp2 = NULL;
  436.     int    savcon;
  437.     int    fd1 = -1,
  438.         fd2 = -1;
  439.  
  440.     /*
  441.      * ... then write to it & close it ...
  442.      */
  443.     (void) (*writefunc)(fp);
  444.     (void) fclose(fp);
  445.  
  446.     /*
  447.      * ... then re-open it for reading, open second one
  448.      * for writing & re-arrange file descriptors.
  449.      *
  450.      * Note that we assume that the editor's standard
  451.      * input, output & error files are the same device,
  452.      * since I can't imagine how any of them could
  453.      * usefully be redirected to anything else.
  454.      */
  455.  
  456. #ifndef _O_BINARY
  457. #    define _O_BINARY 0
  458. #endif
  459. #ifndef _O_EXCL
  460. #    define _O_EXCL 0
  461. #endif
  462.     if (
  463.         (savcon = dup(0)) < 3
  464.         ||
  465.         (fd1 = open(temp1, _O_RDONLY | _O_BINARY)) < 3
  466.         ||
  467.         (temp2 = tempfname("xvi_in")) == NULL 
  468.         ||
  469.         (fd2 = open(temp2, _O_WRONLY|_O_CREAT|_O_EXCL|_O_BINARY, 0600)) < 3
  470.     ) {
  471.         retval = FALSE;
  472.     } else {
  473.         (void) dup2(fd1, 0);
  474.         (void) dup2(fd2, 1);
  475.         (void) dup2(fd2, 2);
  476.  
  477.         (void) close(fd1);
  478.         (void) close(fd2);
  479.         fd1 = fd2 = -1;
  480.  
  481.         /*
  482.          * Run the command.
  483.          */
  484.         (void) system(cmd);
  485.  
  486.         /*
  487.          * Restore our standard input, output & error
  488.          * files.
  489.          */
  490.         (void) dup2(savcon, 0);
  491.         (void) dup2(savcon, 1);
  492.         (void) dup2(savcon, 2);
  493.  
  494.         /*
  495.          * Now read from the second temporary file,
  496.          * close it, & we're done.
  497.          */
  498.         if ((fp = fopen(temp2, "r")) == NULL) {
  499.         retval = FALSE;
  500.         } else {
  501.         (void) (*readfunc)(fp);
  502.         (void) fclose(fp);
  503.         retval = TRUE;
  504.         }
  505.     }
  506.     /*
  507.      * Clean up.
  508.      */
  509.     if (temp2) {
  510.         (void) remove(temp2);
  511.         free(temp2);
  512.     }
  513.     if (savcon > 2)
  514.         (void) close(savcon);
  515.     if (fd1 > 2)
  516.         (void) close(fd1);
  517.     if (fd2 > 2)
  518.         (void) close(fd2);
  519.     }
  520.  
  521.     if (temp1) {
  522.     (void) remove(temp1);
  523.     free(temp1);
  524.     }
  525.  
  526.     return(retval);
  527. }
  528.  
  529. /*
  530.  * The following functions are untested because neither of us has
  531.  * access to an MS-DOS compiler at the moment.
  532.  */
  533.  
  534. /*
  535.  * Expand environment variables in filename.
  536.  */
  537.  
  538. #define VMETACHAR    '$'
  539.  
  540. static char *
  541. vexpand(name)
  542.     char        *name;
  543. {
  544.     static Flexbuf    b;
  545.     register char    *p1, *p2;
  546.  
  547.     if ((p2 = strchr(name, VMETACHAR)) == (char *) NULL) {
  548.     return name;
  549.     }
  550.     flexclear(&b);
  551.     p1 = name;
  552.     while (*p1) {
  553.     register int    c;
  554.     register char    *val;
  555.     Flexbuf        vname;
  556.  
  557.     while (p1 < p2) {
  558.         (void) flexaddch(&b, *p1++);
  559.     }
  560.     flexnew(&vname);
  561.     for (p2++; (c = *p2) != '\0' && (is_alnum(c) || c == '_'); p2++) {
  562.         (void) flexaddch(&vname, c);
  563.     }
  564.     if (!flexempty(&vname)
  565.         &&
  566.         (val = getenv(flexgetstr(&vname))) != (char *) NULL) {
  567.         while ((c = *val++) != '\0') {
  568.         (void) flexaddch(&b, c);
  569.         }
  570.         p1 = p2;
  571.     }
  572.     flexdelete(&vname);
  573.     if ((p2 = strchr(p1, VMETACHAR)) == (char *) NULL) {
  574.         while ((c = *p1) != '\0') {
  575.         (void) flexaddch(&b, c);
  576.         p1++;
  577.         }
  578.     }
  579.     }
  580.     return flexgetstr(&b);
  581. }
  582.  
  583. int
  584. inchar(long mstimeout)
  585. {
  586.     DWORD timedout;
  587.  
  588.     timedout = WaitForSingleObject(hStdIn,mstimeout==0 ? INFINITE : (DWORD)mstimeout);
  589.     if ( timedout != 0 )
  590.         return EOF;
  591.     else {
  592.         INPUT_RECORD rec;
  593.         DWORD nread;
  594.         BOOL r;
  595.  
  596.         r = ReadConsoleInput(hStdIn,&rec,1,&nread);
  597.         switch(rec.EventType) {
  598.         case KEY_EVENT:
  599.             return virtkey(rec.Event.KeyEvent);
  600.             break;
  601.         case MOUSE_EVENT:
  602.             break;
  603.         case WINDOW_BUFFER_SIZE_EVENT:
  604.             break;
  605.         case MENU_EVENT:
  606.             break;
  607.         case FOCUS_EVENT:
  608.             break;
  609.         }
  610.         return EOF;
  611.     }
  612. }
  613.  
  614. int
  615. myGetchar(void)
  616. {
  617.     CHAR chBuf;
  618.     DWORD dwRead;
  619.     int r;
  620.  
  621.     r = ReadFile(hStdIn, &chBuf, sizeof(chBuf), &dwRead, NULL);
  622.     if ( !r )
  623.         return -1;
  624.     else
  625.         return (int)chBuf;
  626. }
  627.  
  628. /********************************************************************
  629. * FUNCTION: perrError(void)                                         *
  630. *                                                                   *
  631. * PURPOSE: beep twice and abort program                             *
  632. *                                                                   *
  633. * INPUT: none                                                       *
  634. *                                                                   *
  635. * RETURNS: none                                                     *
  636. *                                                                   *
  637. * COMMENTS: only called from perr() when a fatal error has occurred *
  638. ********************************************************************/
  639.  
  640. void perrError(void)
  641. {
  642.   DWORD dwLastError; /* for debugger purposes */
  643.  
  644.   dwLastError = GetLastError();
  645.   /* we can't output an error message - beep instead */
  646.   Beep(600, 200);
  647.   Beep(700, 200);
  648.   exit(1);
  649. }
  650.  
  651.  
  652. /*********************************************************************
  653. * FUNCTION: perr(PCHAR szFileName, int line, PCHAR szApiName,        *
  654. *                DWORD dwError)                                      *
  655. *                                                                    *
  656. * PURPOSE: report API errors. Allocate a new console buffer, display *
  657. *          error number and error text, restore previous console     *
  658. *          buffer                                                    *
  659. *                                                                    *
  660. * INPUT: current source file name, current line number, name of the  *
  661. *        API that failed, and the error number                       *
  662. *                                                                    *
  663. * RETURNS: none                                                      *
  664. *********************************************************************/
  665.  
  666. /* this is the size of the buffer for the error message text */
  667. #define MSG_BUF_SIZE 512
  668.  
  669. void perr(PCHAR szFileName, int line, PCHAR szApiName, DWORD dwError)
  670. {
  671.   BOOL bSuccess;
  672.   HANDLE hConTemp; /* temp console buffer to put error message on */
  673.   HANDLE hCurrentCon; /* to save the current console so we can restore it */
  674.   CHAR szTemp[256];
  675.   DWORD cCharsWritten;
  676.   DWORD dwLastError;  /* for debugging purposes */
  677.   CHAR msgBuf[MSG_BUF_SIZE]; /* buffer for message text from system */
  678.  
  679.   /* create a handle to the current console buffer */
  680.   hCurrentCon = CreateFile("CONOUT$", GENERIC_WRITE, FILE_SHARE_READ |
  681.      FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  682.   if (hCurrentCon == INVALID_HANDLE_VALUE)
  683.     {
  684.     dwLastError = GetLastError();
  685.     /* just in case this works */
  686.     printf("Fatal error in perr() on line %d: %d\n", __LINE__, dwLastError);
  687.     Beep(600, 200);
  688.     exit(1);
  689.     }
  690.   /* create a temporary console buffer that we can write on */
  691.   hConTemp = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
  692.       FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CONSOLE_TEXTMODE_BUFFER,
  693.       NULL);
  694.   if (hConTemp == INVALID_HANDLE_VALUE)
  695.     {
  696.     dwLastError = GetLastError();
  697.     printf("Fatal error in perr() on line %d: %d\n", __LINE__, dwLastError);
  698.     Beep(600, 200);
  699.     exit(1);
  700.     }
  701.   /* make the temp buffer to the current buffer */
  702.   bSuccess = SetConsoleActiveScreenBuffer(hConTemp);
  703.   if (!bSuccess)
  704.     {
  705.     dwLastError = GetLastError();
  706.     printf("Fatal error in perr() on line %d: %d\n", __LINE__, dwLastError);
  707.     Beep(600, 200);
  708.     exit(1);
  709.     }
  710.   /* set red on white text for future text output */
  711.   SetConsoleTextAttribute(hConTemp, FOREGROUND_RED | BACKGROUND_WHITE);
  712.   /* format our error message */
  713.   sprintf(szTemp, "%s: Error %d from %s on line %d\n", szFileName,
  714.       dwError, szApiName, line);
  715.   /* write the message to the console */
  716.   bSuccess = WriteFile(hConTemp, szTemp, strlen(szTemp), &cCharsWritten,
  717.       NULL);
  718.   if (!bSuccess)
  719.     perrError();
  720.   memset(msgBuf, 0, sizeof(msgBuf));
  721.   /* get the text description for that error number from the system */
  722.   cCharsWritten = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
  723.       getConX(hConTemp), NULL, dwError, MAKELANGID(0, SUBLANG_ENGLISH_US),
  724.       msgBuf, sizeof(msgBuf), NULL);
  725.   if (!cCharsWritten)
  726.     perrError();
  727.   else
  728.     {
  729.     /* write the text description to the console */
  730.     bSuccess = WriteFile(hConTemp, msgBuf, strlen(msgBuf), &cCharsWritten,
  731.         NULL);
  732.     if (!bSuccess)
  733.       perrError();
  734.     }
  735.   myGetchar();
  736.   /* reset the current buffer to the original one */
  737.   bSuccess = SetConsoleActiveScreenBuffer(hCurrentCon);
  738.   if (!bSuccess)
  739.     perrError();
  740.   CloseHandle(hConTemp);
  741.   bSuccess = CloseHandle(hCurrentCon);
  742.   if (!bSuccess)
  743.     perrError();
  744.   return;
  745. }
  746.  
  747. void
  748. scroll_down(unsigned start_row, unsigned end_row, int nlines)
  749. {
  750.     SMALL_RECT rect;
  751.     COORD newcoord;
  752.     CHAR_INFO charinfo;
  753.  
  754.     rect.Top = start_row;
  755.     rect.Bottom = end_row;
  756.     rect.Left = 0;
  757.     rect.Right = WindowSize.X - 1;
  758.     newcoord.X = 0;
  759.     newcoord.Y = start_row + nlines;
  760.     charinfo.Char.AsciiChar = ' ';
  761.     charinfo.Attributes = 0;
  762.     ScrollConsoleScreenBuffer(hStdOut,&rect,&rect,newcoord,&charinfo);
  763. }
  764.  
  765. void
  766. scroll_up(unsigned start_row, unsigned end_row, int nlines)
  767. {
  768.     scroll_down(start_row,end_row,-nlines);
  769. }
  770.  
  771. void
  772. set_colour(int n)
  773. {
  774.     int c;
  775.     switch(n){
  776.     case 0: c = FOREGROUND_WHITE; break;
  777.     case 1: c = FOREGROUND_GREEN; break;
  778.     case 2: c = FOREGROUND_BLUE; break;
  779.     case 3: c = FOREGROUND_RED; break;
  780.     case 4: c = FOREGROUND_RED|FOREGROUND_GREEN; break;
  781.     case 5: c = FOREGROUND_RED|FOREGROUND_BLUE; break;
  782.     case 6: c = FOREGROUND_GREEN|FOREGROUND_BLUE; break;
  783.     case 7: c = FOREGROUND_BLUE; break;
  784.     case 8: c = FOREGROUND_GREEN; break;
  785.     case 9: c = FOREGROUND_RED; break;
  786.     case 10: c = FOREGROUND_RED; break;
  787.     default: c = FOREGROUND_WHITE; break;
  788.     }
  789.     Currcolor = c;
  790.     settextcolor();
  791. }
  792.  
  793. struct vkinfo {
  794.     CHAR val;
  795. } vktable[] = {
  796.     0,    /* 0x00 */
  797.     0,    /* 0x01 VK_LBUTTON */
  798.     0,    /* 0x02 VK_RBUTTON */
  799.     0,    /* 0x03 VK_CANCEL */
  800.     0,    /* 0x04 VK_MBUTTON */
  801.     0,    /* 0x05 */
  802.     0,    /* 0x06 */
  803.     0,    /* 0x07 */
  804.     '\b',    /* 0x08 VK_BACK */
  805.     '\t',    /* 0x09 VK_TAB */
  806.     0,    /* 0x0a */
  807.     0,    /* 0x0b */
  808.     0,    /* 0x0c VK_CLEAR */
  809.     '\r',    /* 0x0d VK_RETURN */
  810.     0,    /* 0x0e */
  811.     0,    /* 0x0f */
  812.  
  813.     0,    /* 0x10 VK_SHIFT */
  814.     0,    /* 0x11 VK_CONTROL */
  815.     0,    /* 0x12 VK_MENU */
  816.     0,    /* 0x13 VK_PAUSE */
  817.     0,    /* 0x14 VK_CAPITAL */
  818.     0,    /* 0x15 */
  819.     0,    /* 0x16 */
  820.     0,    /* 0x17 */
  821.     0,    /* 0x18 */
  822.     0,    /* 0x19 */
  823.     0,    /* 0x1a */
  824.     '\033',    /* 0x1b VK_ESCAPE */
  825.     0,    /* 0x1c */
  826.     0,    /* 0x1d */
  827.     0,    /* 0x1e */
  828.     0,    /* 0x1f */
  829.  
  830.     ' ',    /* 0x20 VK_SPACE */
  831.     0,    /* 0x21 VK_PRIOR */
  832.     0,    /* 0x22 VK_NEXT */
  833.     0,    /* 0x23 VK_END */
  834.     0,    /* 0x24 VK_HOME */
  835.     'h',    /* 0x25 VK_LEFT */
  836.     'k',    /* 0x26 VK_UP */
  837.     'l',    /* 0x27 VK_RIGHT */
  838.     'j',    /* 0x28 VK_DOWN */
  839.     0,    /* 0x29 VK_SELECT */
  840.     0,    /* 0x2a VK_PRINT */
  841.     0,    /* 0x2b VK_EXECUTE */
  842.     0,    /* 0x2c VK_SNAPSHOT or VK_COPY */
  843.     0,    /* 0x2d VK_INSERT */
  844.     0,    /* 0x2e VK_DELETE */
  845.     0,    /* 0x2f VK_HELP */
  846.  
  847.     0,    /* 0x30 '0' */
  848.     0,    /* 0x31 '1' */
  849.     0,    /* 0x32 '2' */
  850.     0,    /* 0x33 '3' */
  851.     0,    /* 0x34 '4' */
  852.     0,    /* 0x35 '5' */
  853.     0,    /* 0x36 '6' */
  854.     0,    /* 0x37 '7' */
  855.     0,    /* 0x38 '8' */
  856.     0,    /* 0x39 '9' */
  857.     0,    /* 0x3a */
  858.     0,    /* 0x3b */
  859.     0,    /* 0x3c */
  860.     0,    /* 0x3d */
  861.     0,    /* 0x3e */
  862.     0,    /* 0x3f */
  863.  
  864.     0,    /* 0x40 */
  865.     0,    /* 0x41 'A' */
  866.     0,    /* 0x42 'B' */
  867.     0,    /* 0x43 'C' */
  868.     0,    /* 0x44 'D' */
  869.     0,    /* 0x45 'E' */
  870.     0,    /* 0x46 'F' */
  871.     0,    /* 0x47 'G' */
  872.     0,    /* 0x48 'H' */
  873.     0,    /* 0x49 'I' */
  874.     0,    /* 0x4a 'J' */
  875.     0,    /* 0x4b 'K' */
  876.     0,    /* 0x4c 'L' */
  877.     0,    /* 0x4d 'M' */
  878.     0,    /* 0x4e 'N' */
  879.     0,    /* 0x4f 'O' */
  880.  
  881.     0,    /* 0x50 'P' */
  882.     0,    /* 0x51 'Q' */
  883.     0,    /* 0x52 'R' */
  884.     0,    /* 0x53 'S' */
  885.     0,    /* 0x54 'T' */
  886.     0,    /* 0x55 'U' */
  887.     0,    /* 0x56 'V' */
  888.     0,    /* 0x57 'W' */
  889.     0,    /* 0x58 'X' */
  890.     0,    /* 0x59 'Y' */
  891.     0,    /* 0x5a 'Z' */
  892.     0,    /* 0x5b */
  893.     0,    /* 0x5c */
  894.     0,    /* 0x5d */
  895.     0,    /* 0x5e */
  896.     0,    /* 0x5f */
  897.  
  898.     '0',    /* 0x60 VK_NUMPAD0 */
  899.     '1',    /* 0x61 VK_NUMPAD1 */
  900.     '2',    /* 0x62 VK_NUMPAD2 */
  901.     '3',    /* 0x63 VK_NUMPAD3 */
  902.     '4',    /* 0x64 VK_NUMPAD4 */
  903.     '5',    /* 0x65 VK_NUMPAD5 */
  904.     '6',    /* 0x66 VK_NUMPAD6 */
  905.     '7',    /* 0x67 VK_NUMPAD7 */
  906.     '8',    /* 0x68 VK_NUMPAD8 */
  907.     '9',    /* 0x69 VK_NUMPAD9 */
  908.     '*',    /* 0x6a VK_MULTIPLY */
  909.     '+',    /* 0x6b VK_ADD */
  910.     0,    /* 0x6c VK_SEPARATOR */
  911.     '-',    /* 0x6d VK_SUBTRACT */
  912.     '.',    /* 0x6e VK_DECIMAL */
  913.     '/',    /* 0x6f VK_DIVIDE */
  914.  
  915.     0,    /* 0x70 VK_F1 */
  916.     0,    /* 0x71 VK_F2 */
  917.     0,    /* 0x72 VK_F3 */
  918.     0,    /* 0x73 VK_F4 */
  919.     0,    /* 0x74 VK_F5 */
  920.     0,    /* 0x75 VK_F6 */
  921.     0,    /* 0x76 VK_F7 */
  922.     0,    /* 0x77 VK_F8 */
  923.     0,    /* 0x78 VK_F9 */
  924.     0,    /* 0x79 VK_F10 */
  925.     0,    /* 0x7a VK_F11 */
  926.     0,    /* 0x7b VK_F12 */
  927.     0,    /* 0x7c VK_F13 */
  928.     0,    /* 0x7d VK_F14 */
  929.     0,    /* 0x7e VK_F15 */
  930.     0,    /* 0x7f VK_F16 */
  931.  
  932.     0,    /* 0x80 VK_F17 */
  933.     0,    /* 0x81 VK_F18 */
  934.     0,    /* 0x82 VK_F19 */
  935.     0,    /* 0x83 VK_F20 */
  936.     0,    /* 0x84 VK_F21 */
  937.     0,    /* 0x85 VK_F22 */
  938.     0,    /* 0x86 VK_F23 */
  939.     0,    /* 0x87 VK_F24 */
  940.     0,    /* 0x88 */
  941.     0,    /* 0x89 */
  942.     0,    /* 0x8a */
  943.     0,    /* 0x8b */
  944.     0,    /* 0x8c */
  945.     0,    /* 0x8d */
  946.     0,    /* 0x8e */
  947.     0,    /* 0x8f */
  948.  
  949.     0,    /* 0x90 VK_NUMLOCK */
  950.     0,    /* 0x91 VK_SCROLL */
  951.     0,    /* 0x92 */
  952.     0,    /* 0x93 */
  953.     0,    /* 0x94 */
  954.     0,    /* 0x95 */
  955.     0,    /* 0x96 */
  956.     0,    /* 0x97 */
  957.     0,    /* 0x98 */
  958.     0,    /* 0x99 */
  959.     0,    /* 0x9a */
  960.     0,    /* 0x9b */
  961.     0,    /* 0x9c */
  962.     0,    /* 0x9d */
  963.     0,    /* 0x9e */
  964.     0    /* 0x9f */
  965. };
  966.  
  967. int
  968. virtkey(KEY_EVENT_RECORD keyrec)
  969. {
  970.     int vksize = sizeof(vktable) / sizeof(struct vkinfo);
  971.     int vk, v;
  972.  
  973.     if ( ! keyrec.bKeyDown )
  974.         return EOF;
  975.  
  976.     if ( keyrec.uChar.AsciiChar != 0 )
  977.         return keyrec.uChar.AsciiChar;
  978.  
  979.     vk = keyrec.wVirtualKeyCode;
  980.     if ( vk < 0 || vk >= vksize )
  981.         return EOF;
  982.  
  983.     v = vktable[vk].val;
  984.     if ( v )
  985.         return v;
  986.     return EOF;
  987. }
  988.